---
title: Controller 开发实践
description: Feat Cloud Controller 注解开发详细教程与最佳实践
sidebar:
    order: 4
---
import CheckAuthorize from '../../../components/CheckAuthorize.astro'
import {TabItem, Tabs, Aside} from "@astrojs/starlight/components";

<CheckAuthorize/>

Feat Cloud 在设计 Controller 注解时，借鉴了 Spring 的设计理念，降低学习成本的同时进行了极致简化，专注于高性能和易用性。

## 注解一览

Feat Cloud 提供以下核心注解：
- [`@Controller`](#controller) - 标记控制器类
- [`@RequestMapping`](#requestmapping) - 定义请求路由
- [`@Param`](#param) - 绑定查询参数
- [`@PathParam`](#pathparam) - 绑定路径参数  
- [`@InterceptorMapping`](#interceptormapping) - 配置拦截器
- [`@PostConstruct`](#生命周期管理) - 初始化回调
- [`@PreDestroy`](#生命周期管理) - 销毁回调

## 注解详解

### `@Controller`
`@Controller` 用于标记一个类为控制器，作为 Spring 风格的注解设计，降低学习成本。

通常与 `@RequestMapping` 一起使用来定义请求映射路径。

**定义：**
```java
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Controller {
    String value() default "";
}
```

**value 参数说明：**
- `value()` 定义了 Controller 的基础路径，其他方法级别的请求路径会基于这个基础路径。
- 默认情况下，value 是一个空字符串，意味着没有基础路径前缀。
- 示例：如果设置 `@Controller("/user")`，那么该 Controller 下的所有方法将通过 `/user` 路径前缀访问。

### `@RequestMapping`
`@RequestMapping` 用于标记方法，表示该方法处理特定的 HTTP 请求路径。

支持指定HTTP请求方法，有效类型：`GET`, `HEAD`, `POST`, `PUT`, `PATCH`, `DELETE`, `OPTIONS`, `TRACE`
**定义：**
```java
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface RequestMapping {
    String value() default "";

    RequestMethod[] method() default {};
}
```

**参数说明：**
- **value**: 指定与该方法关联的 URL 路径，默认为空字符串，意味着使用 Controller 级别的路径前缀（如果有）。
- **method**: 指定该方法支持的 HTTP 请求类型，默认不限制请求方法，即所有类型都可以访问该方法。

### `@Param`
`@Param` 用于绑定请求中的查询参数（Query Parameters）到方法参数上。当方法参数为自定义POJO类型时，可无需使用`@Param`注解，框架会自动进行参数绑定。

**定义：**
```java
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface Param {
    /**
     * 指定要绑定的查询参数的名称。
     *
     * @return 参数名称
     */
    String value();
}
```
**value 参数说明：**
- 如果 HTTP 请求中没有与 `value` 匹配的查询参数，则方法参数将被赋予默认值（如 null、0 或 false，取决于类型）。
- 示例：若方法定义为 `@Param("id") int userId`，则会尝试从请求中获取名为 `id` 的参数并转换为整数赋值给 `userId`。

### `@PathParam`
`@PathParam` 用于绑定请求中的路径参数（Path Parameters）到方法参数上。

**定义：**
```java
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface PathParam {
    /**
     * 参数名称
     *
     * @return 参数名称
     */
    String value();
}
```

**value 参数说明：** 指定要绑定的路径参数的名称，该名称必须与路由中定义的占位符名称一致。
- 示例：若 URL 定义为 `/user/{id}`，则应使用 `@PathParam("id")` 来绑定路径中的 `id` 值到方法参数。
- 如果 HTTP 请求路径中没有匹配的参数名，则框架会抛出异常或自动赋予默认值（如 null、0 等），具体行为取决于框架实现和参数类型。

### `@InterceptorMapping`
`@InterceptorMapping` 用于为特定的请求路径添加拦截器逻辑，在请求到达目标方法之前执行一些操作。

**定义：**
```java
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.SOURCE)
@Documented
public @interface InterceptorMapping {

    String[] value() default "";
}
```

**value 参数说明：** 指定拦截器应用的 URL 路径模式数组。
- 支持精确匹配（如 `/path`）和通配符匹配（如 `/path/*`）。
- 如果未指定，默认为空数组，表示拦截器不匹配任何请求。
- 示例：若设置 `@InterceptorMapping({"/user", "/api/*"})`，则该拦截器会作用于所有 `/user` 和 `/api` 下的请求路径。

## 实践示例

基于项目中的实际示例，创建一个简单的 Controller：

```java
@Controller("demo2")
public class Demo2Controller {
    
    // 基础路径映射
    @RequestMapping("")
    public String test1() {
        return "hello world";
    }
    
    // 支持查询参数
    @RequestMapping("/param1")
    public String test2(@Param("param") String param) {
        return "hello " + param;
    }
    
    // 多个查询参数
    @RequestMapping("/param2")
    public String test3(@Param("param1") String param1, @Param("param2") String param2) {
        return "hello " + param1 + " " + param2;
    }
    
    // 支持对象参数绑定
    @RequestMapping("/param3")
    public String test4(TestParam param) {
        return "hello " + param.getParam1() + " " + param.getParam2();
    }
    
    // 命名参数对象
    @RequestMapping("/param4")
    public String test5(@Param("param") TestParam param) {
        return "hello param is " + param;
    }
}
```

对应的参数对象：

```java
public class TestParam {
    private String param1;
    private String param2;
    
    // getters and setters
    public String getParam1() { return param1; }
    public void setParam1(String param1) { this.param1 = param1; }
    
    public String getParam2() { return param2; }
    public void setParam2(String param2) { this.param2 = param2; }
}
```

## 生命周期管理

Feat Cloud 支持 Bean 生命周期注解，可以在适当的时机执行初始化和清理逻辑：

```java
@Controller
class Demo1Controller {

    @PostConstruct
    public void init() {
        System.out.println("Controller 初始化完成");
    }

    @RequestMapping
    public String test1() {
        return "hello";
    }

    @PreDestroy
    public void destroy() {
        System.out.println("Controller 正在销毁");
    }
}
```

## 拦截器使用

通过 `@InterceptorMapping` 注解可以为特定路径添加拦截器：

```java
@Controller("api")
public class ApiController {
    
    @RequestMapping("/users")
    public String getUsers() {
        return "用户列表";
    }
    
    // 为API路径添加拦截器
    @InterceptorMapping({"/api/users/*", "/api/admin/*"})
    public Interceptor authInterceptor() {
        return (context, completableFuture, chain) -> {
            // 前置处理：权限验证
            String token = context.Request.getHeader("Authorization");
            if (token == null || !isValidToken(token)) {
                context.Response.setStatus(401);
                context.Response.write("Unauthorized".getBytes());
                completableFuture.complete(null);
                return;
            }
            
            // 继续执行请求链
            chain.proceed(context, completableFuture);
            
            // 后置处理：记录访问日志
            System.out.println("API 访问完成: " + context.Request.getRequestURI());
        };
    }
    
    private boolean isValidToken(String token) {
        // 实际的 token 验证逻辑
        return "valid-token".equals(token);
    }
}
```

**拦截器使用注意事项：**
- **路径匹配**：支持精确匹配和通配符匹配（`*`）
- **执行顺序**：多个拦截器按定义顺序执行
- **异步处理**：正确处理 `CompletableFuture` 以支持异步请求
- **异常处理**：拦截器内需要处理可能的异常情况

